#ifndef FACEBOOK_H
#define FACEBOOK_H

#include <string>
#include <vector>
#include <map>
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>
#include <curl/curl.h>
#include <curl/easy.h>

namespace Facebook
{
     using namespace std;
     using namespace boost;
	    
     
     struct User;
     struct Status;
     struct Comment;
     typedef vector<Status> Statuses;
     typedef vector<User>   Users;
     typedef vector<Comment> Comments;
	
     typedef vector<string> StringList;
     typedef map<string, string> StringMap;
     typedef pair<bool, bool> BoolPair;


     string urlEncode(const string& in);
     string gbk(const string& s);
     string utf8(const string& s);
     time_t getStdTime(const string& created_at) ;
     int toInt(const string& str);

     int countAt(const string& s);
     
     struct User 
     {
          User();
          User(const StringMap& m);
          ~User();

          void set(const StringMap& m);
          
          string id;
          string screen_name;
          string province;
          string city;
          string location;
          string description;
          string url;
          string profile_image_url;
          string domain;
          string gender;
          int    followers_count;
          int	   friends_count;
          int    statuses_count;
          int    favourites_count;
          string created_at;
          string verified;
          boost::shared_ptr<Status> status;
     };


     struct Status
     {
          Status();
          Status(const StringMap& sm, const StringMap& um);
          ~Status();
		
          void   set(const StringMap& sm, const StringMap& um);
		
          void   setProperties(const StringMap& sm);
          void   setUser(const StringMap& um);
          void   setRT(const StringMap& rtsm, const StringMap& rtum);
		
          string created_at;
          string id;
          string text;
          string thumbnail_pic;
          string bmiddle_pic;
          string original_pic;
          User   user;
          string in_reply_to_screen_name;
          string in_reply_to_user_id;
          string in_reply_to_status_id;
          string source;
          string   truncated;
          string   favorited;
          boost::shared_ptr<Status> retweeted_status;
     };


     struct Comment
     {
          Comment();
          ~Comment();
	
          void	setProperties(const StringMap& cm);
          void 	setReply(const StringMap& rem);

          string	id;
          string	text;
          string	favorited;
          string	truncated;
          string	created_at;
          string 	source;
          User	user;
          Status	status;
          boost::shared_ptr<Comment> reply_comment;
     };

        
     /*
     class BasicXMLParser : noncopyable
     {
     public:
          BasicXMLParser();
          virtual ~BasicXMLParser();
          bool   parse(const string& xmlstr); 
          void   node2map(TiXmlElement* node, StringMap& m);
          void   node2map(TiXmlElement* node, const string& value,
                          StringMap& m);

          void   node2maplist(TiXmlElement* node, const string& value, 
                              vector<StringMap >& vm); 
          void   getRootNode(TiXmlElement* & root);
			
          void   parseXml2Users(const string& xml, Users& us);
          void   parseXml2Statuses(const string& xml, Statuses& ss); 
          void   parseXml2Status(const string& xml, Status& s);
          void   parseXml2Comments(const string& xml, Comments& cs);
	
          void   parseNode2Status(TiXmlElement* node, Status& s);
          void   parseNode2User(TiXmlElement* node, User& u);
          void   parseNode2Comment(TiXmlElement* node, Comment& c);

     private:
          TiXmlDocument  xmldoc;
     };
    */


     class Curltools : boost::noncopyable
     {
     public:

          Curltools();
          ~Curltools();

          string  postDelete(const string& url);
          string    postContent(const string& url, const string& content);

          // !!!!reimplement this function
          string  postFile(const string& url, const string& content, const string& filename);
          
          string    getURL(const string& url);
          string  getURL(const string& url, const string& oAuthHttpHeader);

          /*
           * bool  oAuthLogin(const string& consumer_key,
                           const string& consumer_secret,
                           const string& filename);
            */
          /*
           *  set the urls
           */
          /*
          void  setAuthorizeUrl(const string& authorize_url);
          void  setRequestTokenUrl(const string& request_token_url);
          void  setAccessTokenUrl(const string& access_token_url);
        */
          //string getUrlWithoutAuth(const string& url);

          int httpCode() const;

     private:

          //bool  oAuthRequestToken(string& authorizeUrl);
          //bool  oAuthAccessToken();

          static int write(char *data, size_t size, size_t nmemb, string *writeData);
          static const int _defaulttimeout = 300;
          static char errorBuffer[CURL_ERROR_SIZE];

          //string m_request_token_url;
          //string m_authorize_url;
          //string m_access_token_url;

          CURL* curl;
          //oAuth m_oAuth;
     };

     
     class Api
     {
     public:


          static int numIgnoredErrors;
          
          //static const string DefaultAppKey;
          Api(const string& format="xml", const int retry_count = 2, 
              const int retry_delay = 60, const int sleep_delay = 360);
			
          ~Api();

          void oAuthLogin(const string& key, const string& secret, const string& filename);
          
          bool 	getPublicTimeline(Statuses& ss);

          /*
            get statues of current user's friends
          */
          bool	getFriendsTimeline(Statuses& ss, 
                                   const string& since_id = "", 
                                   const string& max_id = "", 
                                   const int page = 1,
                                   const int count = 200,
                                   const string& feature = "0");


          bool	getRepostTimeline(Statuses& ss,
                                  const string& status_id, 
                                  const string& since_id = "", 
                                  const string& max_id = "", 
                                  const int page = 1,
                                  const int count = 20);

          
          bool	getCommentsTimeline(Comments& cs, 
                                    const string& since_id = "", 
                                    const string& max_id = "", 
                                    const int page = 1,
                                    const int count = 200);

          
          bool	getUserTimeline(Statuses& ss,
                                const string& user_id = "",
                                const string& screen_name = "", 
                                const string& since_id = "",
                                const string& max_id = "", 	
                                const int page = 1,
                                const int count = 200);
          /*
            get the statuses @me
          */
          bool	getMentionsAtMe(Statuses& ss, 
                                const string& since_id="", 
                                const string& max_id =  "", 
                                const int page = 1,
                                const int count = 200); 
          /*
            get comments of a status
          */
          bool	getComments(Comments& c,
                            const string& status_id,
                            const int page = 1,
                            const int count = 200);

          /*
            get counts of RTs and comments of statuses.
            Note: counts will be accumulated!
          */
          bool	getRTCounts(map<string, pair<int,int> >& counts,
                            const StringList& status_ids);


          // !!!! test
          bool verifyAccount(User& u);
          
          bool	showUser(User& u, 
                         const string& user_id, 
                         const string& screen_name = "");
          bool	showStatus(Status& s, const string& status_id);
          bool	deleteStatus(const string& status_id);
          /*
            update status !!!!!
          */
          bool	updateStatus(const string& content, const string& filename = "");
          bool	RTStatus(const string& status_id, const string& content = "");
          bool	commentStatus(const string& status_id, const string& content);

          /*
            check whether from_userid is following to_userid
            default from_userid is yourself
			   
          */
          bool	followship(BoolPair& followship,
                           const string& to_userid, 
                           const string& from_userid = "");

          bool	follow(const string& user_id, const string& screen_name = "");
          bool	unfollow(const string& user_id, const string& screen_name = "");
	
          bool	searchUsers(Users& us,
							string& query,
							bool sort_by_time = true, // when false, use sort_by_followers
							const int page = 1,
							const int count = 200);
          bool	searchStatuses(Statuses& ss,
                               string& query, 
                               bool sort_by_time = true,
                               const int page = 1,
                               const string& user_id = "",
                               const string& starttime = "",
                               const string& endtime = "", 
                               const int count = 200);
							

          /*
            get the current statuses of one's friends
          */
          bool	getFriends(Users& us, 
                           const string& user_id, 
                           const string& screen_name = "", 
                           const string& cursor = "-1", 
                           const int count = 200);
          /*	
            get the current statuses of one's followers
          */
          bool	getFollowers(Users& us, 
                             const string& user_id,
                             const string& screen_name = "", 
                             const string& cursor = "-1", 
                             const int count = 200,
                             const int total = 10000);


          bool	getFriendIds(StringList& idlist, 
                             const string& user_id,
                             const string& screen_name = "", 
                             const string& cursor = "-1", 
                             const int count = 5000);	

          bool	getFollowerIds(StringList& idlist, 
                               const string& user_id,
                               const string& screen_name = "", 
                               const string& cursor = "-1", 
                               const int count = 5000);

          int rateLimitStatus(int& reset_time);


          const string& getUserName() const
          { return username; }
          
          
          bool	logout();
     protected:

          int 	checkError(const string& url, string& page); 

     private:
          static string errorCode;
          static string errorInfo;
          
          bool waitOnHttpError();
          
          boost::shared_ptr<Curltools> 		m_curl;
		
          string format;	//json or xml
          string username;
          string password;
          string appkey;
          string urlprefix;
          int    retry_count;
          int    retry_delay;
          int    sleep_delay;
     };




     void displayStatus(const Status& s);
     void displayStatusShort(const Status& s); // short version
     void displayComment(const Comment& c);
     void displayUser(const User& user);
     
}


#endif // TSINA_H
